package org.liziyi.pss;

import org.liziyi.pss.common.enums.ScheduleCallbackEnum;
import org.liziyi.pss.config.Config;
import org.liziyi.pss.entity.ProcessControlBlock;
import org.liziyi.pss.gui.*;
import org.liziyi.pss.model.memory.FirstAdaptMemory;
import org.liziyi.pss.model.schedule.MainScheduler;
import org.liziyi.pss.util.ProcessUtil;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import java.awt.*;
import java.util.Optional;

import static org.liziyi.pss.common.enums.ScheduleCallbackEnum.*;

public class PssApplication extends JFrame {
    private final MainGUI mainGUI;
    private final OperateGUI operateGUI;
    private final MainScheduler scheduler;

    public PssApplication() {
        super("进程调度模拟器");
        mainGUI = new MainGUI();
        operateGUI = new OperateGUI();
        scheduler = new MainScheduler();
        init();
    }
    public void showGUI() {
        this.setVisible(true);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setResizable(false);
        this.setSize(Config.mainGUIWidth, Config.mainGUIHeight);
        this.setLocationRelativeTo(null);
        mainGUI.memoryAreaJPanel.setPreferredSize(new Dimension((int)(Config.mainGUIWidth*0.20),Config.mainGUIHeight)); ;
        mainGUI.operateAreaJPanel.setPreferredSize(new Dimension((int)(Config.mainGUIWidth*0.2),(int)(Config.mainGUIHeight*0.2))); ;
        mainGUI.cpuAreaJPanel.setPreferredSize(new Dimension((int)(Config.mainGUIWidth*0.6),(int)(Config.mainGUIHeight*0.2))); ;
    }

    /**
     * 组件装配 (将cpu区域，内存区域，队列区域，操作区域组装到主面板); 注册回调
     */
    public void init(){
        mainGUI.memoryAreaJPanel.add(((FirstAdaptMemory)scheduler.getMemory()).getMemoryGUI().baseJPanel,BorderLayout.CENTER);
        mainGUI.operateAreaJPanel.add(operateGUI.baseJPanel,BorderLayout.CENTER);
        mainGUI.cpuAreaJPanel.add(scheduler.getCpu().getCpuGUI().baseJPanel,BorderLayout.CENTER);
        mainGUI.queueAreaJPanel.add(scheduler.getScheduleQueueGroup().getQueueGroupGUI().baseJPanel,BorderLayout.CENTER);
        this.add(mainGUI.baseJPanel);
        registerOperateGUICallBack(operateGUI);
    }
    private void registerOperateGUICallBack(OperateGUI gui){
        // 点击添加一个进程
        gui.getAddProcessJButton().addActionListener(actionEvent->{
            ProcessCreateDialogGUI dialog=new ProcessCreateDialogGUI(this,"添加一个进程");
            ((DefaultTableModel)dialog.getProcessJTable().getModel()).addRow(
                    ProcessUtil.pcb2TableRow(ProcessUtil.generateRandomProcess(1,false)[0]));
            registerProcessCreateDialogGUICallBack(dialog);
            dialog.setVisible(true);
            dialog.setSize(this.getSize().width/2,this.getSize().height/4);
            dialog.setLocationRelativeTo(this);
            dialog.setResizable(false);
        });
        // 点击批量添加进程
        gui.getBatchAddProcessJButton().addActionListener(actionEvent -> {
            int processNum=(int)operateGUI.getProcessNumJSpinner().getValue();
            if(processNum<=0){
                JOptionPane.showMessageDialog(this,"批量生成的进程数必须大于0","警告",JOptionPane.ERROR_MESSAGE);
                return;
            }
            ProcessCreateDialogGUI dialog=new ProcessCreateDialogGUI(this,"批量添加进程");
            ProcessControlBlock[] pcbs=ProcessUtil.generateRandomProcess(processNum,operateGUI.getHasSyncProcessJCheckBox().isSelected());
            DefaultTableModel model=(DefaultTableModel)dialog.getProcessJTable().getModel();
            for(int i=0;i<pcbs.length;i++){
                model.addRow(ProcessUtil.pcb2TableRow(pcbs[i]));
            }
            registerProcessCreateDialogGUICallBack(dialog);
            dialog.setVisible(true);
            dialog.setSize(this.getSize().width/2,this.getSize().height/2);
            dialog.setLocationRelativeTo(this);
            dialog.setResizable(false);
        });
        // 点击启动
        gui.getStartJButton().addActionListener(actionEvent->{
            // 注册回调，当进程被加入到就绪队列时，添加到可挂起进程下拉框中
            scheduler.addScheduleListener(SCHEDULE_FROM_BACKOP_TO_READY, pid -> {
                SwingUtilities.invokeLater(()->operateGUI.getProcessNotSuspendJComboBox().addItem(pid));
            });
            scheduler.addScheduleListener(SCHEDULE_FROM_RUNNING_TO_FINISH,pid->{
                SwingUtilities.invokeLater(()->operateGUI.getProcessNotSuspendJComboBox().removeItem(pid));
            });
            scheduler.schedule();
        });
        // 点击重置
        gui.getResetJButton().addActionListener(actionEvent->{
            scheduler.reset();
        });
        // 点击挂起进程
        gui.getSuspendJButton().addActionListener(actionEvent->{
            scheduler.addScheduleListener(SCHEDULE_FROM_READY_TO_SUSPEND,pid ->{
                SwingUtilities.invokeLater(()-> {
                    operateGUI.getProcessNotSuspendJComboBox().removeItem(pid);
                    operateGUI.getProcessSuspendedJComboBox().addItem(pid);
                });
            });
            Optional.ofNullable(operateGUI.getProcessNotSuspendJComboBox().getSelectedItem())
                    .ifPresent(item->scheduler.suspendProcess((int)item));
        });
        // 点击解挂进程
        gui.getUnSuspendJButton().addActionListener(actionEvent -> {
            scheduler.addScheduleListener(SCHEDULE_FROM_SUSPEND_TO_READY,pid ->{
                SwingUtilities.invokeLater(()-> {
                    operateGUI.getProcessSuspendedJComboBox().removeItem(pid);
                    operateGUI.getProcessNotSuspendJComboBox().addItem(pid);
                });
            });
            Optional.ofNullable(operateGUI.getProcessSuspendedJComboBox().getSelectedItem())
                            .ifPresent(item -> scheduler.unSuspendProcess((int)item));
        });
    }
    private void registerProcessCreateDialogGUICallBack(ProcessCreateDialogGUI gui){
        gui.getOkJButton().addActionListener(actionEvent->{
            TableModel model=gui.getProcessJTable().getModel();
            ProcessControlBlock[] pcbs=ProcessUtil.tableRow2Pcb(model);
            for(ProcessControlBlock pcb:pcbs){
                scheduler.addProcess(pcb);
            }
            gui.dispose();
        });
        gui.getCancelJButton().addActionListener(actionEvent->{
            // 如果已经生成了进程，但取消了，则回退新增的pid
            ProcessUtil.rollbackPid(gui.getProcessJTable().getModel().getRowCount());
            gui.dispose();
        });

    }

}
